#include <stdlib.h>
#include "Date.h"
/**
 * A global variable to store day string representation
 */
char* DayString[] = {"Ahad  ", "Senin ", "Selasa", "Rabu  ", "Kamis ", "Jumat ", "Sabtu ", "Undef "};
/**
 * Fungsi untuk membuat Date
 * @param year Tahun Date
 * @param month Bulan Date
 * @param day Day Date
 * @return Date yang terdiri year, month dan day
 */
Date MakeDate(int year, int month, int day) {
    Date d;
    if (IsDateValid(year, month, day)) {
        SetYear(year, &d);
        SetMonth(month, &d);
        SetDay(day, &d);
        return d;
    } else {
        SetYear(0, &d);
        SetMonth(0, &d);
        SetDay(0, &d);
        printf("Date Not Valid");
    }
    return d;
}
/**
 * Check if a date has valid value
 * @param year Year of associated Date
 * @param month Month of associated Date
 * @param day Day of associated Date
 * @return true if Date valid
 */
int IsDateValid(int year, int month, int day) {
    //check month
    if (year > 1900 && month > 0 || day > 0) {
        if (month <= 12) {
            /*if month is January, March, May, July,August, October or December, 
            there are 31 days*/
            if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                if (day > 31) {
                    return false;
                } else {
                    return true;
                }
                /*if month is not January, March, May, July,August, October or December, 
             and not February there are 30 days */
            } else if (month == 4 || month == 6 || month == 9 || month == 11) {
                if (day > 30) {
                    return false;
                } else {
                    return true;
                }
                //special case for february
            } else {
                if (IsYearKabisat(year)) {
                    /* In Kabisat year, February has 29 days*/
                    if (day > 29) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (day > 28) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        }
    } else {
        return false;
    }
}
/**
 * Get year part of Date
 * @param d Associated Date
 * @return Year part of Date
 */
int GetYear(Date d) {
    return d.year;
}
/**
 * Get Month Part of Date
 * @param d Associated Date
 * @return Month part of date
 */
int GetMonth(Date d) {
    return d.month;
}
/**
 * Get Day part of Date
 * @param d Associated Date
 * @return Day part of Date
 */
int GetDay(Date d) {
    return d.day;
}
/**
 * I.S : Independent
 * F.S : Year value of date set to year
 * @param year Year value
 * @param d Associated Date
 */
void SetYear(int year, Date* d) {
    d->year = year;
}
/**
 * I.S : Independent
 * F.S : Month value of date set to month
 * @param month Month Value
 * @param d Associated Date
 */
void SetMonth(int month, Date* d) {
    d->month = month;
}
/**
 * I.S : Independent
 * F.S : Day value of date set to day
 * @param day Day Value
 * @param d Associated Date
 */
void SetDay(int day, Date* d) {
    d->day = day;
}
/**
 * Check if given year is Kabisat
 * @param year Given year
 * @return true if year is Kabisat
 */
boolean IsYearKabisat(int year) {
    if (year % 4 == 0) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    } else {
        return false;
    }
}
/**
 * I.S : Independent
 * F.S : Print date to screen
 * @param d Associated Date
 */
void PrintDate(Date d) {
    printf("Date: %02i-%02i-%02i\n", GetYear(d), GetMonth(d), GetDay(d));
}
/**
 * Get Second from Date started from year 1900
 * @param d Associated Date
 * @return Seconds that has been passed
 */
long long DateToSecond(Date d) {
    //if given date year is kabisat then it has 365 days, from 1900    
    int year = GetYear(d);
    long long seconds = 0;
    int i;
    for (i = 1900; i < year; i++) {
        if (IsYearKabisat(i)) {
            seconds = seconds + KABISAT_DAYS * 24 * 60 * 60;
        } else {
            seconds = seconds + COMMON_DAYS * 24 * 60 * 60;
        }
    }
    Date startCountDate = MakeDate(year, 1, 1);
    int diff = 0;
    while(!EqDay(startCountDate,d)){
        diff = diff + 1;
        startCountDate = NextDay(startCountDate);
    }
    seconds = seconds + diff * 24 * 60 * 60;
    return seconds;
}
/**
 * Check if a date is less than another Date
 * @param d1 First Date
 * @param d2 Second Date
 * @return true if d1 < d2
 */
boolean DateLessThan(Date d1, Date d2) {
    if(GetYear(d1) < GetYear(d2)){
        return true;
    }else if(GetYear(d1) == GetYear(d2)){
        if(GetMonth(d1) < GetMonth(d2)){
            return true;
        }else if(GetMonth(d1) == GetMonth(d2)){
            if(GetDay(d1) < GetDay(d2)){
                return true;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }else{
        return false;
    }
}
/**
 * Check if d1 is equal with d2
 * @param d1 First date
 * @param d2 Second Date
 * @return true if d1 = d2
 */
boolean EqDay(Date d1, Date d2) {
    return (GetYear(d1) == GetYear(d2) && GetMonth(d1) == GetMonth(d2)
            && GetDay(d1) == GetDay(d2));
}
/**
 * Returns next day of given date
 * @param d Now
 * @return Tomorrow
 */
Date NextDay(Date d) {
    //if month is January, March, May, July, August, Or October
    int month = GetMonth(d);
    if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
        if (GetDay(d) == 31) {
            /* If december then new year :p */
            if (month == 12) {
                return MakeDate(GetYear(d) + 1, 1, 1);
            } else {
                return MakeDate(GetYear(d), GetMonth(d) + 1, 1);
            }
        } else {
            return MakeDate(GetYear(d), GetMonth(d), GetDay(d) + 1);
        }
    }/* if month is not January, March, May, July,August, October or December, 
             and not February */
    else if (month == 4 || month == 6 || month == 9 || month == 11) {
        if (GetDay(d) == 30) {
            return MakeDate(GetYear(d), month + 1, 1);
        } else {
            return MakeDate(GetYear(d), month, GetDay(d) + 1);
        }
        /* Special Case for February */
    } else {
        if (IsYearKabisat(GetYear(d))) {
            if (GetDay(d) == 29) {
                return MakeDate(GetYear(d), GetMonth(d) + 1, 1);
            } else {
                return MakeDate(GetYear(d), GetMonth(d), GetDay(d) + 1);
            }
        } else {
            if (GetDay(d) == 28) {
                return MakeDate(GetYear(d), GetMonth(d) + 1, 1);
            } else {
                return MakeDate(GetYear(d), GetMonth(d), GetDay(d) + 1);
            }
        }
    }
}
/**
 * Returns previous day of given date
 * @param d Now
 * @return Yesterday
 */
Date Yesterday(Date d) {
    int month = GetMonth(d);
    if (GetDay(d) == 1) {
        /* Months which previous month has 30 days*/
        if (month == 1 || month == 5 || month == 7 || month == 10) {
            return MakeDate(GetYear(d), GetMonth(d) - 1, 30);
        }/* Months which previous month has 31 days*/
        else if (month == 2 || month == 4 || month == 6 || month == 9 || month == 11 || month == 8) {
            return MakeDate(GetYear(d), month - 1, 31);
        }/* New Year */
        else if (month == 12) {
            return MakeDate(GetYear(d) - 1, 12, 31);
        }/* Previous month is February which means current month is March, (you don't say?? :p)*/
        else {
            if (IsYearKabisat(GetYear(d))) {
                return MakeDate(GetYear(d), 2, 29);
            } else {
                return MakeDate(GetYear(d), 2, 28);
            }
        }
    } else {
        return MakeDate(GetYear(d), GetMonth(d), GetDay(d) - 1);
    }
}
/**
 * Add D1 with D2
 * @param d1 First Date
 * @param d2 Second Date
 * @return D1 + D2
 */
Date AddDate(Date d1, Date d2) {
    return SecondToDate(DateToSecond(d1) + DateToSecond(d2));
}
/**
 * I.S : d1 and d2 is defined
 * F.S : d1 = d1 + d2
 * @param d1 First Date
 * @param d2 Second Date
 */
void AfterDate(Date* d1, Date d2) {
    *d1 = SecondToDate(DateToSecond(*d1) + DateToSecond(d2));
}
/**
 * Returns absolute difference value in d1 and d2
 * @param d1 First day
 * @param d2 Second day
 * @return sum of day of differences
 */
int DaysDiff(Date d1, Date d2) {
    long long diff = DateToSecond(d1) - DateToSecond(d2);
    if (diff < 0) {
        diff = -diff;
    }
    int totalDays = diff / (24 * 60 * 60);
    if (diff % (24 * 60 * 60) != 0) {
        totalDays = totalDays + 1;
    }
    return totalDays;
}
/**
 * Returns Date with given seconds elapsed since 1900
 * @param seconds seconds elapsed
 * @return Date with elapsed seconds = seconds
 */
Date SecondToDate(long long seconds) {
    /* 1 day = 24 hour x 60 minutes x 60 seconds */
    Date d = MakeDate(1900, 1, 1);
    int totalDays = seconds / (24 * 60 * 60);
    if (seconds % (24 * 60 * 60) != 0) {
        totalDays = totalDays + 1;
    }
    int i;
    for (i = 0; i < totalDays; i++) {
        d = NextDay(d);
    }
    return d;
}

/**
 * Returs first Day in the given year and month
 * @param date date
 * @return day of date
 */
Day GetFirstDay(int year, int month) {
    int dayDiff = DaysDiff(MakeDate(1900, 1, 1), MakeDate(year, month, 1));
    return (dayDiff % 7) + 1;
}

/**
 * Returns the string representation of day
 * @param day day
 * @return string of day
 */
char* GetDayString(Day day) {
    return DayString[day];
}

/**
 * Returns maximum day count in a month
 * @param month month 
 * @param year year
 * @return count of days
 */
int MaxDaysInMonth(int month, int year) {
    switch (month) {
        case 1:
        {
            return 31;
        }
        case 2:
        {
            if (IsYearKabisat(year)) {
                return 29;
            } else {
                return 28;
            }
        }
        case 3:
        {
            return 31;
        };
        case 4:
        {
            return 30;
        };
        case 5:
        {
            return 31;
        };
        case 6:
        {
            return 30;
        };
        case 7:
        {
            return 31;
        };
        case 8:
        {
            return 31;
        };
        case 9:
        {
            return 30;
        };
        case 10:
        {
            return 31;
        };
        case 11:
        {
            return 30;
        };
        case 12:
        {
            return 31;
        };
        default:
        {
            return 0;
        }
    }
}

